
package com.google.android.apps.wakemewhere.mapsearch;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.google.android.maps.GeoPoint;

import android.util.Pair;

public class BusinessSearch implements SearchResultProvider {
	
	private static final String STRIP_FROM_JSON_RESPONSE = "while(1);";
	private static final String TAG_BUSINESS_NAME = "gw:businessname";
	private static final String TAG_BUSINESS_ADDRESS = "gw:address";

	private static final Logger logger = Logger.getLogger(BusinessSearch.class.getName());
	
	private static final int RESULT_PRIORITY = 10;

	public void asyncSearch(final String query, final SearchResultCallback callback, final Object tag, final GeoPoint mapLowerLeft, final GeoPoint mapUpperRight) {
		Runnable searchRunnable = new Runnable() {
			public void run() {
				List<SearchResult> result = search(query);
				callback.onSearchResults(result, tag, BusinessSearch.this, true);
			}};
		new Thread(searchRunnable).start();
	}

	private List<SearchResult> search(String query) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet("http://maps.google.com/maps?output=json&q=" + URLEncoder.encode(query));
		
		try {
			HttpResponse response = httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity == null) {
				logger.warning("No HTTP entity");
				return Collections.emptyList();
			}
			String responseJson = EntityUtils.toString(entity);
			if (responseJson.startsWith(STRIP_FROM_JSON_RESPONSE)) {
				responseJson = responseJson.substring(STRIP_FROM_JSON_RESPONSE.length());
			}
			return extractBusinessDetails(responseJson);
		} catch (IOException e) {
			logger.warning(e.getMessage());
		}
		return Collections.emptyList();
	}
	
	private static List<SearchResult> extractBusinessDetails(String json) {
		List<SearchResult> out = new ArrayList<SearchResult>();
		json = json.replace("\\", "");
		int position = 0;
		while (true) {
			Pair<Integer, GeoPoint> latlng = findLatLng(json, position);
			if (latlng == null) {
				return out;
			}
			logger.info("Got latlng for Business: " + latlng.second);
			position = latlng.first;			
			Pair<Integer, String> name = getTagValue(json, TAG_BUSINESS_NAME, position);
			if (name == null) {
				return out;
			}
			position = name.first;
			Pair<Integer, String> address = getTagValue(json, TAG_BUSINESS_ADDRESS, position);
			if (address == null) {
				return out;
			}
			out.add(new SearchResult(name.second, address.second, RESULT_PRIORITY, latlng.second));
			position = address.first;
		}
	}
	
	private static final String LATLNG_TAG = ",latlng:{lat:";
	private static final String LATLNG_SEPARATOR = ",lng:";
	private static Pair<Integer, GeoPoint> findLatLng(String json, int position) {
		// ,latlng:{lat:51.417194000000002,lng:-0.081431000000000003},
		int start = json.indexOf(LATLNG_TAG, position);
		if (start == -1) {
			return null;
		}
		int comma = json.indexOf(',', start + LATLNG_TAG.length());
		if (comma == -1) {
			return null;
		}
		if (comma <= start) {
			return null;
		}
		String lat = json.substring(start + LATLNG_TAG.length(), comma);
		int lngPosition = comma + LATLNG_SEPARATOR.length();
		int closingBrace = json.indexOf('}', lngPosition);
		if (closingBrace == -1) {
			return null;
		}
		if (closingBrace <= comma) {
			return null;
		}
		String lng = json.substring(lngPosition, closingBrace);
		try {
			double latDouble = Double.parseDouble(lat);
			double lngDouble = Double.parseDouble(lng);
			return new Pair<Integer, GeoPoint>(
					closingBrace, 
	                new GeoPoint((int)(latDouble * 1E6), (int)(lngDouble * 1E6)));			
		} catch (NumberFormatException e) {
			logger.warning("Could not parse lat,lng: [" + lat + "],[" + lng + "] exception=" + e.getMessage());
			return null;
		}
	}
	
	private static Pair<Integer, String> getTagValue(String json, String tag, int start) {
		int position = json.indexOf(tag, start) + tag.length();
		if (position == -1) {
			return null;
		}
		if (position + 2 >= json.length() || json.charAt(position) != '=' || json.charAt(position + 1) != '"') {
			return null;
		}
		int endQuote = json.indexOf("\"", position + 2);
		if (endQuote == -1) {
			return null;
		}
		String value = json.substring(position + 2, endQuote);
		value = unescapeString(value);
		return new Pair<Integer, String>(endQuote, value);
	}
	
	private static String unescapeString(String in) {
		return in.replace("x26#39;", "'").replace("x26amp;", "&");
	}
}
